import os
import sys

def getRecall(tp, fn):
    try:
        recall = tp/float(tp+fn)
    except ZeroDivisionError:
        #print "ZeroDivisionError catched, recall = 0 replaced"
        recall = 0.0
    return recall        
    
def getPrecision(tp, fp):
    try:
        precision = tp/float(tp+fp)
    except ZeroDivisionError:
        #print "ZeroDivisionError catched, precision = 0 replaced"
        precision = 0.0
    return precision

def getFvalue(recall, precision, beta = 1.0):
    try:
        beta_2 = beta**2
        f = (1 + beta_2)*precision*recall / (beta_2*precision + recall)
    except ZeroDivisionError:
        #print "ZeroDivisionError catched, f = 0 replaced"
        f = 0.0
    return f

def getStandardDeviation(samples):
    """
    1.Compute the mean for the data set.
    2.Compute the deviation by subtracting the mean from each value.
    3.Square each individual deviation.
    4.Add up the squared deviations.
    5.Divide by one less than the sample size.
    6.Take the square root. 
    """
    N = len(samples)
    mean = float(sum(samples))/N
    deviations = [(sample - mean) for sample in samples]
    squared_deviations = [d**2.0 for d in deviations]
    return (sum(squared_deviations)/(N-1))**0.5

def getTpTnFpFnTuple(savedPath, iter, code, machinename, ranked = None):
    resultList = []
    if machinename not in ('weka', 'wekaig'):
        try:
            testFile = open('%s/%s/test_%s.dat'%(savedPath, iter, code),'rU')
            empiricalList = []
            for line in testFile:    
                empiricalList.append(int(line.split()[0]))
        finally:
            testFile.close()
    
        predictions = open('%s/%s/%s.%s'%(savedPath, iter, code, filetype),'rU').read().split()
        predictionList = []
        for value in predictions:
            if float(value) > 0:
                predictionList.append(1)
            else:
                predictionList.append(-1)    
        for i in range(len(predictions)):
            resultList.append((empiricalList[i],predictionList[i]))
    elif machinename in ('weka', 'wekaig'):
        try:
            if machinename == 'wekaig':
                predictionFile = open('%s/%s/test_%s_ig_%s.%s'%(savedPath, iter, code, ranked, filetype), 'rU')
            else:
                predictionFile = open('%s/%s/test_%s.%s'%(savedPath, iter, code, filetype), 'rU')                
            predictionFile.readline()
            for line in predictionFile:
                #sys.stderr.write(line)
                li = line.split()                
                if len(li) >= 3:
                    actual = int(li[1].split(":")[1])
                    predicted= int(li[2].split(":")[1])
                    resultList.append((actual,predicted))
        finally: predictionFile.close()
    tp = resultList.count((1,1))
    tn = resultList.count((-1,-1))
    fp = resultList.count((-1,1))
    fn = resultList.count((1,-1))
    return (tp, tn, fp, fn)

        

savedPath = sys.argv[1]
machinename = sys.argv[2].lower()
ranked = None
if machinename == 'svm': filetype = 'SvmPrediction'
elif machinename == 'maxent': filetype = 'MaxentPrediction'
elif machinename == 'weka': filetype = 'Prediction'
elif machinename == 'wekaig': 
    filetype = 'Prediction'
    ranked = sys.argv[3]
else:
    print "Don't know which Machine Learner was used. Please choose one."
    print "'svm', 'maxent', or weka_application"
    sys.exit(1)

codesList = open('config/manual/focusedClasses.txt','rU').read().split()
iters = os.listdir(savedPath)
iters = [i for i in iters if os.path.isdir(os.path.join(savedPath,i))]
#print "iters:", iters
coderesults = []
coderesults_without_others = []

system_tps_with_others = []
system_fns_with_others = []
system_fps_with_others = []
system_f_values_with_others = []

system_tps_without_others = []
system_fns_without_others = []
system_fps_without_others = []
system_f_values_without_others = []
for code in codesList: #<for:code>
    recalls = []
    precisions = []
    f_values = []
    #print "****** code: %s *****"%code
    (tps,tns,fps,fns)=([],[],[],[])
    for iter in iters:
        (tp,tn,fp,fn) = getTpTnFpFnTuple(savedPath, iter, code, machinename, ranked)
        recall = getRecall(tp, fn)
        precision = getPrecision(tp, fp)
        f_value = getFvalue(recall, precision, 1.0)
        #print "\t----- iter %s: ----- "%iter
        #print "tp=%d,tn=%d,fp=%d,fn=%d (/times)"%(tp,tn,fp,fn)        
        #print "\t recall = %.4f \n\t precision = %.4f"%( recall, precision)
        #print "\t f_value = %.4f"%f_value
        recalls.append(recall)
        precisions.append(precision)
        f_values.append(f_value)
        tps.append(tp)
        tns.append(tn)
        fps.append(fp)
        fns.append(fn)        
        
    averageRecall = getRecall(sum(tps), sum(fns))
    averagePrecision = getPrecision(sum(tps), sum(fps))
    averageFvalue = getFvalue(averageRecall, averagePrecision)

    recallStdDev = getStandardDeviation(recalls)
    precisionStdDev = getStandardDeviation(precisions)
    averageFvalueStdDev = getStandardDeviation(f_values)
    
    system_tps_with_others.append(tps)
    system_fns_with_others.append(fns)
    system_fps_with_others.append(fps)
    system_f_values_with_others.append(averageFvalue)
    if code != "others":
        system_tps_without_others.append(tps)
        system_fns_without_others.append(fns)
        system_fps_without_others.append(fps)
        system_f_values_without_others.append(averageFvalue)
    
    coderesult = (code, 
                  averageRecall, recallStdDev, 
                  averagePrecision, precisionStdDev, 
                  averageFvalue, averageFvalueStdDev)
    #print "code \"%s\":  averageRecall = %.4f  averagePrecision = %.4f  averageFvalue = %.4f"%coderesult
    #print "\"%s\"\n %.4f\n %.4f\n %.4f\n"%coderesult
    #print ""
    coderesults.append(coderesult)
#</for:code>

system_recall_with_others = getRecall(sum([sum(li) for li in system_tps_with_others]),
                                      sum([sum(li) for li in system_fns_with_others]))
system_precision_with_others = getPrecision(sum([sum(li) for li in system_tps_with_others]),
                                      sum([sum(li) for li in system_fps_with_others]))
system_micro_f_value_with_others = getFvalue(system_recall_with_others, system_precision_with_others)
system_macro_f_value_with_others = sum(system_f_values_with_others)/len(system_f_values_with_others)


system_recall_without_others = getRecall(sum([sum(li) for li in system_tps_without_others]),
                                      sum([sum(li) for li in system_fns_without_others]))
system_precision_without_others = getPrecision(sum([sum(li) for li in system_tps_without_others]),
                                      sum([sum(li) for li in system_fps_without_others]))
system_micro_f_value_without_others = getFvalue(system_recall_without_others, system_precision_without_others)
system_macro_f_value_without_others = sum(system_f_values_without_others)/len(system_f_values_without_others)

micro_systemResult = ("Micro_10", 
                      system_recall_with_others, '/',  
                      system_precision_with_others, '/',
                      system_micro_f_value_with_others, '/')

macro_systemResult = ("Macro_10",
                      '/','/',
                      '/','/',
                      system_macro_f_value_with_others,getStandardDeviation(system_f_values_with_others))

micro_systemResultWithoutOthers = ("Micro_9", 
                                   system_recall_without_others, '/', 
                                   system_precision_without_others, '/', 
                                   system_micro_f_value_without_others, '/')
macro_systemResultWithoutOthers = ("Macro_9", 
                                   '/', '/', 
                                   '/', '/', 
                                   system_macro_f_value_without_others, getStandardDeviation(system_f_values_without_others))

coderesults.append(micro_systemResult)
coderesults.append(macro_systemResult)

coderesults.append(micro_systemResultWithoutOthers)
coderesults.append(macro_systemResultWithoutOthers)

print ' '.join(sys.argv)

for i in xrange(len(coderesult)):
    line = []
    for result in coderesults:
        line.append(str(result[i]))
    print '\t'.join(line)
print ''





"""
Original/Predicted	Class	 		not(Class)(=Other)
Class			TP ~ True Positive)	FN ~ False Negative
not(Class) (=Other)	FP ~ False Positive 	TN ~ True Negative

(1) Recall = TP / (TP + FN) (=Sensitivity)

(2) Precision = TP / (TP + FP)

[ F-value = ]

(3) Specificity = TN / (TN + FN)

(4) Accuracy = (TP + TN) / N = diag(n[k,k]) 


"""
